home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE11 / AUTOSAVE / AUTOSAVE.ZIP / autosave.pas next >
Encoding:
Pascal/Delphi Source File  |  1996-06-08  |  17.1 KB  |  483 lines

  1. {+------------------------------------------------------------
  2.  | Library AutoSave
  3.  |
  4.  | Version: 1.0  Created: 06/07/96, 12:26:09
  5.  |               Last Modified: 06/07/96, 12:26:09
  6.  | Author : P. Below
  7.  | Project: Autosave expert for Delphi 1.0
  8.  | Description:
  9.  |   Autosave expert for Delphi. Provides automatic saving of
  10.  |   files on loss of focus and reload on gain of focus.
  11.  |   This is a DLL expert, install it by inserting a line
  12.  |   like the following (path adjusted, of course) into your
  13.  |   Delphi.INI, in the [Experts] section:
  14.  |
  15.  |   autosave=f:\delphi\projects\experts\autosave.dll
  16.  |
  17.  |   You will find a new menu item under the Help menu that will
  18.  |   read "Disable Autosave" or "Enable Autosave", depending on
  19.  |   the state of the expert. The expert is enabled at startup.
  20.  |
  21.  | I noticed some timing problems with this expert using Delphi 
  22.  | 1.0 and CodeWright 32 on Windows NT 3.51. The "save files on
  23.  | loss of focus" option in CW32 actually does the save as a 
  24.  | background thread so Delphi will be active before the save 
  25.  | has been completed and as a consequence the files date/time
  26.  | stamp has not yet changed! Consequence: failure to reload 
  27.  | the file in Delphi. The switch backwards can have similar
  28.  | problems, probably because of NTs lazy buffering scheme on 
  29.  | disk writes. Switching buffers in CW will cause the reload
  30.  | to occur. Manually saving the file in CW before switching
  31.  | to Delphi takes care of the first problem. 
  32.  | Not a very satisfactory situation but as a temporary measure
  33.  | the options to save or restore all open files is provided 
  34.  | in two additional exports.
  35.  +------------------------------------------------------------}
  36. Library AutoSave;
  37.  
  38. Uses Messages, Wintypes, WinProcs, SysUtils, ToolIntf, ExptIntf;
  39.  
  40. Type
  41.   TAutoSaveExpert = class(TIExpert)
  42.   private
  43.     FIsActive: Boolean;             { state flag }
  44.     FToolServices: TIToolServices;  { Delphi service requester }
  45.     Procedure SetIsActive( state: Boolean );
  46.   public
  47.     { Expert UI strings }
  48.     Constructor Create( TS: TIToolServices );
  49.     function GetName: string; override;
  50.     function GetComment: string; override;
  51.     function GetGlyph: HBITMAP; override;
  52.     function GetStyle: TExpertStyle; override;
  53.     function GetState: TExpertState; override;
  54.     function GetIDString: string; override;
  55.     function GetMenuText: string; override;
  56.  
  57.     { Launch the Expert }
  58.     procedure Execute; override;
  59.     
  60.     { Procedures called on loss and gain of focus }
  61.     Procedure SaveAllFiles;
  62.     Procedure RestoreAllFiles;
  63.  
  64.     property IsActive: Boolean read FIsActive write SetIsActive;
  65.   end;
  66.  
  67.   TSaveAllExpert = class(TIExpert)
  68.   public
  69.     { Expert UI strings }
  70.     function GetName: string; override;
  71.     function GetComment: string; override;
  72.     function GetGlyph: HBITMAP; override;
  73.     function GetStyle: TExpertStyle; override;
  74.     function GetState: TExpertState; override;
  75.     function GetIDString: string; override;
  76.     function GetMenuText: string; override;
  77.  
  78.     { Launch the Expert }
  79.     procedure Execute; override;
  80.   end;
  81.  
  82.   TRestoreAllExpert = class(TIExpert)
  83.   public
  84.     { Expert UI strings }
  85.     function GetName: string; override;
  86.     function GetComment: string; override;
  87.     function GetGlyph: HBITMAP; override;
  88.     function GetStyle: TExpertStyle; override;
  89.     function GetState: TExpertState; override;
  90.     function GetIDString: string; override;
  91.     function GetMenuText: string; override;
  92.  
  93.     { Launch the Expert }
  94.     procedure Execute; override;
  95.   end;
  96.  
  97. Var
  98.   TheExpert: TAutoSaveExpert;
  99.   SAExpert : TSaveAllExpert;
  100.   RAExpert : TRestoreAllExpert;
  101.   OldWndProc: TFarProc;
  102.   hDelphi: HWND;
  103.  
  104.  
  105. {+-----------------------------------------------------------------------
  106.  | The expert subclasses the Delphi application window and waits for     
  107.  | WM_ACTIVATEAPP messages to arrive. Depending on the wparam of this    
  108.  | message it will call the SaveAllFiles or RestoreAllFiles methods of   
  109.  | the expert. The expert calls two service procedures, HookDelphi and   
  110.  | UnhookDelphi, if its state changes from inactive to active and back.  
  111.  | These tow procedures use API functions to find the Delphi application 
  112.  | window and subclass it.                                               
  113.  +----------------------------------------------------------------------}
  114.  
  115. {+-EnumProc-----------------------------------------------------------
  116.  | This function is a callback used with EnumTaskWindows to find the  
  117.  | Delphi application window. It just checks the class name of the    
  118.  | passed window. If that is TApplication the window handle is returned
  119.  | and enumeration stops.
  120.  +-------------------------------------------------------------------}
  121.  Function EnumProc( aWnd: HWnd; Var foundHwnd: HWND ): Bool; export;
  122.   Var
  123.     buf: Array [0..40] of Char;
  124.   Begin
  125.     buf[0] := #0;
  126.     GetClassName( aWnd, buf, 41 );
  127.     buf[40]:= #0;
  128.     Result := StrIComp(buf, 'TApplication') <> 0;
  129.     If not Result Then 
  130.       foundHWnd := aWnd;
  131.   End; { EnumProc }
  132.   
  133. {+-FindDelphiApp--------------------------------------------------------
  134.  | This function is called by HookDelphi to find the Delphi application 
  135.  | window. It returns the handle of this window, or 0, if the window    
  136.  | could not be found (highly unlikely).                                
  137.  +---------------------------------------------------------------------}
  138.  Function FindDelphiApp: HWND;
  139.   Begin
  140.     Result := 0;
  141.     EnumTaskWindows( GetCurrentTask, @EnumProc, LongInt(@Result));
  142.   End; { FindDelphiApp }
  143.   
  144. {+-UnHookDelphi----------------------------------------------------------
  145.  | This procedure undoes the subclassing for the Delphi application      
  146.  | window. It is called either when the state of the expert changes to   
  147.  | inactive or when Delphi is going down and the replacement window proc 
  148.  | sees a WM_DESTROY.                                                    
  149.  +----------------------------------------------------------------------}
  150.  Procedure UnHookDelphi;
  151.   Begin
  152.     If hDelphi <> 0 Then Begin
  153.       SetWindowLong( hDelphi, GWL_WNDPROC, LongInt(OldWndProc));
  154.       OldWndProc := Nil;
  155.       hDelphi := 0;
  156.     End; { If }
  157.   End; { UnHookDelphi }
  158.  
  159. {+-WndProc--------------------------------------------------------------
  160.  | This is the replacement window procedure used for the Delphi         
  161.  | application window. It passes all messages to the original window    
  162.  | proc. WM_ACTIVATEAPP and WM_DESTROY are acted upon.                  
  163.  | The function will see WM_ACTIVATEAPP with wparam = 0 if Delphi is    
  164.  | loosing the focus. We save all files in this case. If wparam is <> 0 
  165.  | Delphi is gaining the focus and we restore all files. As an          
  166.  | additional safeguard the function will undo the subclassing on       
  167.  | WM_DESTROY. That should have happend before this message arrives,    
  168.  | however (see FinshExpert).                                           
  169.  +---------------------------------------------------------------------}
  170.  Function WndProc(aWnd: HWND; aMsg, wparam: Word; lparam: LongInt):
  171.   LongInt; export; 
  172.   Begin
  173.     If aMsg = WM_ACTIVATEAPP Then Begin
  174.       If wparam = 0 Then 
  175.         TheExpert.SaveAllFiles
  176.       Else
  177.         TheExpert.RestoreAllFiles;
  178.     End { If }
  179.     Else
  180.       If aMsg = WM_DESTROY Then Begin
  181.         { Call old winproc first because UnHookdelphi will set OldWndProc
  182.           to Nil! }
  183.         Result := CallWindowProc( OldWndProc, aWnd, aMsg, wparam, lparam );
  184.         UnHookDelphi;
  185.         Exit;
  186.       End; { If }
  187.     Result := CallWindowProc( OldWndProc, aWnd, aMsg, wparam, lparam );
  188.   End; { WndProc }
  189.   
  190. {+-HookDelphi--------------------------------------------------------
  191.  | This procedure does the subclassing for the Delphi application    
  192.  | window. It is called when the state of the expert changes to   
  193.  | active.
  194.  +-------------------------------------------------------------------}
  195. Procedure HookDelphi;
  196.   Begin
  197.     hDelphi := FindDelphiApp;
  198.     If hDelphi <> 0 Then Begin
  199.       OldWndProc := Pointer(
  200.         SetWindowLong( hDelphi, GWL_WNDPROC, LongInt(@WndProc)))
  201.     End; { If }
  202.   End; { HookDelphi }
  203.  
  204. {+----------------------------
  205.  | Methods of TAutoSaveExpert
  206.  +---------------------------}
  207.  
  208. {+-Create-------------------------------------------------------------
  209.  | Create the expert, save the passed reference to the Delphi service 
  210.  | provider, activate the expert, which causes the subclassing to be  
  211.  | performed.                                                         
  212.  +-------------------------------------------------------------------}
  213.  Constructor TAutoSaveExpert.Create( TS: TIToolServices );
  214.   Begin
  215.     inherited Create;
  216.     FToolServices := TS;
  217.     isActive := True;
  218.   End; { TAutoSaveExpert.Create }
  219.   
  220. { The following are the standard methods required of an expert. }
  221. function TAutoSaveExpert.GetName: string;  
  222.   Begin
  223.     Result := 'AutoSave Expert';
  224.   End; { TAutoSaveExpert.GetName }
  225.   
  226. function TAutoSaveExpert.GetComment: string;  
  227.   Begin
  228.     Result := EmptyStr;
  229.   End; { TAutoSaveExpert.GetComment }
  230.   
  231. function TAutoSaveExpert.GetGlyph: HBITMAP;  
  232.   Begin
  233.     Result := 0;
  234.   End; { TAutoSaveExpert.GetGlyph }
  235.   
  236. function TAutoSaveExpert.GetStyle: TExpertStyle;  
  237.   Begin
  238.     Result := esStandard;
  239.   End; { TAutoSaveExpert.GetStyle }
  240.   
  241. function TAutoSaveExpert.GetState: TExpertState;  
  242.   Begin
  243.     Result := [esEnabled];
  244.   End; { TAutoSaveExpert.GetState }
  245.   
  246. function TAutoSaveExpert.GetIDString: string;  
  247.   Begin
  248.     Result := 'PBelow.AutoSaveExpert';
  249.   End; { TAutoSaveExpert.GetIDString }
  250.   
  251. function TAutoSaveExpert.GetMenuText: string;  
  252.   Begin
  253.     If IsActive Then 
  254.       Result := 'Disable AutoSave'
  255.     Else
  256.       Result := 'Enable AutoSave';
  257.   End; { TAutoSaveExpert.GetMenuText }
  258.  
  259. {+-Execute------------------------------------------------------------
  260.  | This method is called if the user selects the menu item for the    
  261.  | expert. This switches the state of the expert and causes a message 
  262.  | to appear.                                                         
  263.  +-------------------------------------------------------------------}
  264. procedure TAutoSaveExpert.Execute;  
  265.   Const
  266.     Messages: Array [Boolean] of Pchar =
  267.       ('Autosave expert has been deactivated.',
  268.        'Autosave expert has been activated.');
  269.   Begin
  270.     IsActive := not IsActive;
  271.     MessageBox( GetActiveWindow, Messages[IsActive], 'AutoSave', 
  272.                 MB_OK or MB_ICONINFORMATION );
  273.   End; { TAutoSaveExpert.Execute }
  274.  
  275. {+-SetIsActive--------------------------------------------------------
  276.  | This method is called when the state of the expert is changed. It  
  277.  | performs the appropriate subclassing or unsubclassing and sets the 
  278.  | FIsActive flag to the new state.                                   
  279.  +-------------------------------------------------------------------}
  280. Procedure TAutoSaveExpert.SetIsActive( state: Boolean );
  281.   Begin
  282.     If state <> FIsActive Then Begin
  283.       If FIsActive Then 
  284.         UnhookDelphi
  285.       Else
  286.         HookDelphi;
  287.       FIsActive := (hDelphi <> 0);
  288.     End; { If }
  289.   End; { TAutoSaveExpert.SetIsActive }
  290.  
  291. {+-SaveAllFiles----------------------------------------------------------
  292.  | This method is called from the replacement window proc on loss of     
  293.  | focus. It saves the project and all open units that belong to the     
  294.  | project. I'm not sure if this loop also saves open files that do not  
  295.  | belong to the project!                                                
  296.  +----------------------------------------------------------------------}
  297. Procedure TAutoSaveExpert.SaveAllFiles;
  298.   Var
  299.     i: Integer;
  300.     S: String;
  301.   Begin
  302.     With FToolServices Do 
  303.       If Length(GetprojectName) > 0 Then Begin
  304.         SaveProject;
  305.         For i := 0 To GetUnitCount-1 Do Begin
  306.           S := GetUnitName(i);
  307.           If IsFileOpen(S) Then
  308.             SaveFile( S );
  309.         End; { For }
  310.       End; { If }
  311.   End; { TAutoSaveExpert.SaveAllFiles }
  312.  
  313. {+-RestoreAllFiles----------------------------------------------------
  314.  | This method is called from the replacement window proc on gain of  
  315.  | focus. It restores all open units that belong to the project.      
  316.  | I'm not sure if this loop also restores open files that do not     
  317.  | belong to the project!                                         
  318.  +----------------------------------------------------------------------}
  319. Procedure TAutoSaveExpert.RestoreAllFiles;
  320.   Var
  321.     i: Integer;
  322.     S: String;
  323.   Begin
  324.     With FToolServices Do Begin
  325.       If Length(GetprojectName) > 0 Then
  326.         For i := 0 To GetUnitCount-1 Do Begin
  327.           S := GetUnitName(i);
  328.           If IsFileOpen(S) Then
  329.             ReloadFile( S );
  330.         End; { For }
  331.     End; { With }
  332.   End; { TAutoSaveExpert.RestoreAllFiles }
  333.  
  334. {+----------------------------
  335.  | Methods of TSaveAllExpert
  336.  +---------------------------}
  337.  
  338. { The following are the standard methods required of an expert. }
  339. function TSaveAllExpert.GetName: string;  
  340.   Begin
  341.     Result := 'SaveAll Expert';
  342.   End; { TSaveAllExpert.GetName }
  343.   
  344. function TSaveAllExpert.GetComment: string;  
  345.   Begin
  346.     Result := EmptyStr;
  347.   End; { TSaveAllExpert.GetComment }
  348.   
  349. function TSaveAllExpert.GetGlyph: HBITMAP;  
  350.   Begin
  351.     Result := 0;
  352.   End; { TSaveAllExpert.GetGlyph }
  353.   
  354. function TSaveAllExpert.GetStyle: TExpertStyle;  
  355.   Begin
  356.     Result := esStandard;
  357.   End; { TSaveAllExpert.GetStyle }
  358.   
  359. function TSaveAllExpert.GetState: TExpertState;  
  360.   Begin
  361.     Result := [esEnabled];
  362.   End; { TSaveAllExpert.GetState }
  363.   
  364. function TSaveAllExpert.GetIDString: string;  
  365.   Begin
  366.     Result := 'PBelow.SaveAllExpert';
  367.   End; { TSaveAllExpert.GetIDString }
  368.   
  369. function TSaveAllExpert.GetMenuText: string;  
  370.   Begin
  371.     Result := 'Save all files';
  372.   End; { TSaveAllExpert.GetMenuText }
  373.  
  374. {+-Execute------------------------------------------------------------
  375.  | This method is called if the user selects the menu item for the    
  376.  | expert. 
  377.  +-------------------------------------------------------------------}
  378. procedure TSaveAllExpert.Execute;  
  379.   Begin
  380.     TheExpert.SaveAllFiles
  381.   End; { TSaveAllExpert.Execute }
  382.  
  383. {+----------------------------
  384.  | Methods of TRestoreAllExpert
  385.  +---------------------------}
  386.  
  387. { The following are the standard methods required of an expert. }
  388. function TRestoreAllExpert.GetName: string;  
  389.   Begin
  390.     Result := 'RestoreAll Expert';
  391.   End; { TRestoreAllExpert.GetName }
  392.   
  393. function TRestoreAllExpert.GetComment: string;  
  394.   Begin
  395.     Result := EmptyStr;
  396.   End; { TRestoreAllExpert.GetComment }
  397.   
  398. function TRestoreAllExpert.GetGlyph: HBITMAP;  
  399.   Begin
  400.     Result := 0;
  401.   End; { TRestoreAllExpert.GetGlyph }
  402.   
  403. function TRestoreAllExpert.GetStyle: TExpertStyle;  
  404.   Begin
  405.     Result := esStandard;
  406.   End; { TRestoreAllExpert.GetStyle }
  407.   
  408. function TRestoreAllExpert.GetState: TExpertState;  
  409.   Begin
  410.     Result := [esEnabled];
  411.   End; { TRestoreAllExpert.GetState }
  412.   
  413. function TRestoreAllExpert.GetIDString: string;  
  414.   Begin
  415.     Result := 'PBelow.RestoreAllExpert';
  416.   End; { TRestoreAllExpert.GetIDString }
  417.   
  418. function TRestoreAllExpert.GetMenuText: string;  
  419.   Begin
  420.     Result := 'Reload all files';
  421.   End; { TRestoreAllExpert.GetMenuText }
  422.  
  423. {+-Execute------------------------------------------------------------
  424.  | This method is called if the user selects the menu item for the    
  425.  | expert. 
  426.  +-------------------------------------------------------------------}
  427. procedure TRestoreAllExpert.Execute;  
  428.   Begin
  429.     TheExpert.RestoreAllFiles
  430.   End; { TRestoreAllExpert.Execute }
  431.  
  432.  
  433. {+-FinishExpert------------------------------------------------------
  434.  | This procedure is a callback called by Delphi when the DLL is about 
  435.  | to be unloaded. It undoes the subclassing and destroys the expert.  
  436.  +--------------------------------------------------------------------}
  437. Procedure FinishExpert; export;
  438.   Begin
  439.     If Assigned(TheExpert) Then
  440.       With TheExpert Do Begin
  441.         IsActive := False;
  442.         Free;
  443.         TheExpert := Nil;
  444.       End; { With }
  445.     SAExpert.Free;
  446.     RAExpert.Free;
  447.   End;
  448.  
  449.  
  450. {+-InitExpert-----------------------------------------------------------
  451.  | This is the entry point for the DLL. It is called by Delphi when the 
  452.  | DLL is loaded. We create the expert object here, store the passed    
  453.  | registerproc and tell Delphi which procedure to call on termination. 
  454.  +---------------------------------------------------------------------}
  455. Function InitExpert(ToolServices: TIToolServices;
  456.     RegisterProc: TExpertRegisterProc;
  457.     var Terminate: TExpertTerminateProc): Boolean; export;
  458.   Begin
  459.     LibraryExpertProc := RegisterProc;
  460.     Terminate := FinishExpert;
  461.     TheExpert := TAutoSaveExpert.Create(ToolServices);
  462.     RegisterLibraryExpert( TheExpert );
  463.     SAExpert := TSaveAllExpert.Create;
  464.     RegisterLibraryExpert( SAExpert );
  465.     RAExpert := TRestoreAllExpert.Create;
  466.     RegisterLibraryExpert( RAExpert );
  467.  
  468.     Result := True;
  469.   End; { InitExpert }
  470.  
  471.  
  472. exports
  473.   InitExpert name ExpertEntryPoint;
  474.   
  475. Begin
  476.   TheExpert:= Nil;;
  477.   SAExpert := Nil;
  478.   RAExpert := Nil;
  479.   OldWndProc:= Nil;
  480.   hDelphi:= 0;
  481. End. { Library AutoSave }
  482.  
  483.